package com.qd.panda.service.mall;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.core.weixinmanager.model.MiddleUser;
import com.cdqidi.core.weixinmanager.service.MiddleUserService;
import com.cdqidi.dto.ApiResult;
import com.cdqidi.dto.ApiResultWrapper;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.SpringContextHolder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qd.common.panda.config.PandaParkConfigDTO;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.stroke.PayParamDTO;
import com.qd.common.panda.domain.entity.stroke.UserStrokeOrderDTO;
import com.qd.common.panda.domain.entity.stroke.UserStrokeOrderMallDTO;
import com.qd.common.sys.domain.entity.login.LoginUserDTO;
import com.qd.common.sys.domain.login.AppUserExtend;
import com.qd.common.sys.domain.login.LoginUser;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.panda.model.Commodity;
import com.qd.panda.model.CommodityStatistics;
import com.qd.panda.model.UserStrokeOrder;
import com.qd.panda.model.UserStrokeOrderMall;
import com.qd.panda.service.AsyncService;
import com.qd.panda.service.commodity.CommodityService;
import com.qd.panda.service.stroke.DisplayUtil;
import com.qd.panda.service.stroke.UserStrokeHolderService;
import com.qd.panda.service.stroke.order.UserStrokeOrderMallService;
import com.qd.panda.service.stroke.order.UserStrokeOrderService;
import com.qd.panda.service.third.dc.DataCenterService;
import com.qd.panda.service.third.dc.dto.OrderReqDTO;
import com.qd.pay.config.PayConstant;
import com.qd.pay.domain.OrderDTO;
import com.qd.pay.service.order.OrderHolderService;
import com.qd.system.model.BaseUserInfo;
import com.qd.system.service.user.BaseUserInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;

/**
 * 商城订单
 *
 * @author sjk
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MallService {
    private final UserStrokeOrderService userStrokeOrderService;
    private final UserStrokeOrderMallService userStrokeOrderMallService;
    private final OrderHolderService orderHolderService;
    private final ObjectMapper objectMapper;
    private final PandaParkConfigDTO pandaParkConfig;
    private final DataCenterService dataCenterService;
    private final CommodityService commodityService;
    private final BaseUserInfoService baseUserInfoService;
    private final AsyncService asyncService;
    @Value("${dc.syncConfigCode}")
    private String syncConfigCode;
    @Resource
    private MiddleUserService middleUserService;


    @Transactional(rollbackFor = Exception.class)
    public PayParamDTO createOrder(MallOrderDTO mallOrderDTO) {
        String orderId = IdWorker.getIdStr();
        LocalDateTime now = LocalDateTime.now();
        Commodity commodity = Optional.ofNullable(commodityService.getById(mallOrderDTO.getCommodityId())).orElseThrow(() -> new ApiException("商品ID值错误"));
        if (commodity.getEnable().equals(ConstantDto.SF0)) {
            throw new ApiException("当前商品已经下架");
        }
        BigDecimal orderAmount = commodity.getCommodityPrice().multiply(new BigDecimal(String.valueOf(mallOrderDTO.getRechargeValue())));
        //精确到2位，向上取整
        BigDecimal payAmount = commodity.getUserPrice().multiply(new BigDecimal(String.valueOf(mallOrderDTO.getRechargeValue()))).setScale(2, RoundingMode.UP);
        BigDecimal discountAmount = orderAmount.subtract(payAmount);
        saveStrokeOrder(orderId, now, orderAmount, payAmount, discountAmount);
        saveUserStrokeOrderMall(orderId, mallOrderDTO, commodity);

        return createOrder(payAmount, orderId);
    }

    public ApiResultWrapper<Object> createDcOrder(MallOrderReqDTO mallOrderReqDTO) {
        Commodity commodity = commodityService.getById(mallOrderReqDTO.getCommodityId());
        if (null == commodity) {
            log.error("商品ID值错误: {}", mallOrderReqDTO.getCommodityId());
            return ApiResult.error("商品ID值错误");
        }
        UserStrokeOrder userStrokeOrder = userStrokeOrderService.getById(mallOrderReqDTO.getOrderId());
        if (null == userStrokeOrder) {
            log.error("订单ID值错误: {}", mallOrderReqDTO.getOrderId());
            return ApiResult.error("订单ID值错误");
        }
        LocalDateTime now = LocalDateTime.now();
        OrderReqDTO req = new OrderReqDTO();
        req.setMobile(Optional.ofNullable(baseUserInfoService.getById(userStrokeOrder.getUserId())).map(BaseUserInfo::getMobile).orElse(""));
        req.setAppId(pandaParkConfig.getDefaultAppId());
        req.setOpenid(Optional.ofNullable(middleUserService.getUniqueByUserIdAndAppId(userStrokeOrder.getUserId(), pandaParkConfig.getDefaultAppId())).map(MiddleUser::getOpenid).orElse(""));
        req.setUserId(userStrokeOrder.getUserId());
        req.setOrderTime(DateUtil.localDateTimeToStr(now));
        req.setThirdPartyOrderId(mallOrderReqDTO.getOrderId());
        req.setSyncConfigCode(syncConfigCode);
        req.setCommodityId(mallOrderReqDTO.getCommodityId());
        req.setCommodityPrice(commodity.getCommodityPrice());
        req.setRechargeAccount(mallOrderReqDTO.getRechargeAccount());
        req.setRechargeValue(new BigDecimal(String.valueOf(mallOrderReqDTO.getRechargeValue())));
        req.setPayAmount(mallOrderReqDTO.getPayAmount());
        req.setCustomerIp(mallOrderReqDTO.getCustomerIp());
        ApiResultWrapper<Object> order = dataCenterService.createOrder(req);
        req.freeData();
        return order;
    }


    private PayParamDTO createOrder(BigDecimal payAmount, String bid) {
        if (!StringUtils.hasLength(pandaParkConfig.getParkPartner())) {
            throw new ApiException("配置支付主商户");
        }
        if (!StringUtils.hasLength(pandaParkConfig.getDefaultSubPartner())) {
            throw new ApiException("配置支付默认子商户");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        OrderDTO orderDto = new OrderDTO();
        orderDto.setThirdPartyOrder(bid)
                .setPayAmount(payAmount)
                .setNotifyUrl(pandaParkConfig.getDomain() + "/gy-panda-park/app/userStroke/notify/payAfter")
                .setType(PayConstant.ORDER_TYPE_3)
                .setAppId(pandaParkConfig.getDefaultAppId())
                .setPartner(pandaParkConfig.getParkPartner())
                .setSubPartner(pandaParkConfig.getParkSubPartner());
        if (null != loginUser) {
            orderDto.setPersonId(loginUser.getUserId());
            orderDto.setOpenid(loginUser.getUser().getAppUserExtend().getOpenid());
        }
        log.info("bid: {},金额: {}", bid, orderDto.getPayAmount());
        ObjectNode attachObjectNode = objectMapper.createObjectNode();
        orderDto.setBody("线上商城").setSubject("购物");
        attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_5);
        orderDto.setAttach(attachObjectNode);
        ObjectNode child = objectMapper.createObjectNode();
        ObjectNode wxPub = objectMapper.createObjectNode();
        wxPub.put(UserStrokeHolderService.AMOUNT, orderDto.getPayAmount().doubleValue());
        wxPub.put("sub_appid", pandaParkConfig.getDefaultAppId());
        if (StringUtils.hasLength(orderDto.getOpenid())) {
            wxPub.put("sub_openid", orderDto.getOpenid());
        }
        child.set("wx_pub", objectMapper.createArrayNode().add(wxPub));
        orderDto.setChannelDetails(child);
        if (SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_DEV)) {
            return new PayParamDTO();
        }
        Map<String, String> params = orderHolderService.createOrder(orderDto);
        if (params.isEmpty()) {
            throw new ApiException("获取支付参数异常");
        }
        PayParamDTO payParamDTO = new PayParamDTO();
        payParamDTO.setAppId(pandaParkConfig.getDefaultAppId())
                .setNonceStr(params.get("nonceStr"))
                .setPrepayId(params.get("package"))
                .setPaySign(params.get("paySign"))
                .setSignType(params.get("signType"))
                .setTimeStamp(params.get("timeStamp"))
                .setThirdPartyOrder(bid);
//        try {
//            log.debug("payParamDTO： {}", JacksonUtil.getObjectMapper().writeValueAsString(payParamDTO));
//        } catch (JsonProcessingException ignored) {
//        }
        payParamDTO.setPayAmount(orderDto.getPayAmount().stripTrailingZeros().toPlainString());
        return payParamDTO;
    }

    public void saveStrokeOrder(String orderId, LocalDateTime now, BigDecimal orderAmount, BigDecimal payAmount, BigDecimal discountAmount) {
        final String userId = SecurityUtils.getLoginUser().getUserId();
        final String nickName = Optional.ofNullable(SecurityUtils.getLoginUser()).map(LoginUser::getUser).map(LoginUserDTO::getAppUserExtend).map(AppUserExtend::getNickName).orElse("");
        final String promotionCode = Optional.ofNullable(SecurityUtils.getLoginUser()).map(LoginUser::getUser).map(LoginUserDTO::getAppUserExtend).map(AppUserExtend::getPromotionCode).orElse("");
        UserStrokeOrderDTO userStrokeOrderDTO = new UserStrokeOrderDTO();
        userStrokeOrderDTO.setOrderId(orderId);
        userStrokeOrderDTO.setBid(orderId);
        userStrokeOrderDTO.setStartTime(now);
        userStrokeOrderDTO.setNickName(nickName);
        userStrokeOrderDTO.setOrderAmount(orderAmount);
        userStrokeOrderDTO.setPayAmount(payAmount);
        userStrokeOrderDTO.setOnLinePayAmount(payAmount);
        userStrokeOrderDTO.setDiscountAmount(discountAmount);
        userStrokeOrderDTO.setPayStatus(DisplayUtil.PAY_STATUS_0);
        userStrokeOrderDTO.setOrderType(DisplayUtil.ORDER_TYPE_5);
        userStrokeOrderDTO.setTripType(DisplayUtil.TRIP_TYPE_4);
        userStrokeOrderDTO.setEtcPay(ConstantDto.SF0);
        userStrokeOrderDTO.setUserId(userId);
        if (StringUtils.hasLength(promotionCode)) {
            userStrokeOrderDTO.setPromotionCode(promotionCode);
        }
        userStrokeOrderService.save(userStrokeOrderDTO);
        userStrokeOrderDTO.freeData();
    }

    public void saveUserStrokeOrderMall(String orderId, MallOrderDTO mallOrderDTO, Commodity commodity) {
        UserStrokeOrderMallDTO userStrokeOrderMallDTO = new UserStrokeOrderMallDTO();
        userStrokeOrderMallDTO.setOrderId(orderId);
        userStrokeOrderMallDTO.setCommodityId(commodity.getCommodityId());
        userStrokeOrderMallDTO.setCommodityPrice(commodity.getCommodityPrice());
        userStrokeOrderMallDTO.setOrderState(DisplayUtil.PAY_STATUS_0);
        userStrokeOrderMallDTO.setCommodityCode(commodity.getCommodityCode());
        userStrokeOrderMallDTO.setCommodityIntroduction(commodity.getCommodityIntroduction());
        userStrokeOrderMallDTO.setQuantity(mallOrderDTO.getRechargeValue());
        userStrokeOrderMallDTO.setCommodityName(commodity.getCommodityName());
        userStrokeOrderMallDTO.setCommodityPic(commodity.getCommodityPic());
        userStrokeOrderMallDTO.setRechargeAccount(mallOrderDTO.getRechargeAccount());
        BigDecimal discountPrice = getDiscountPrice(commodity, mallOrderDTO.getRechargeValue());
        userStrokeOrderMallDTO.setDiscountPrice(discountPrice);
        userStrokeOrderMallDTO.setProductForm(commodity.getProductForm());
        userStrokeOrderMallDTO.setRetry(ConstantDto.SF0);
        userStrokeOrderMallService.save(userStrokeOrderMallDTO);
        userStrokeOrderMallDTO.freeData();
    }

    private static BigDecimal getDiscountPrice(Commodity commodity, Long quantity) {
        if (commodity.getDiscountMethods().equals(PandaConstant.DISCOUNT_METHODS_1)) {
            return commodity.getCommodityPrice().multiply(commodity.getDiscountValue()).multiply(new BigDecimal(quantity)).divide(new BigDecimal("100"), 3, RoundingMode.HALF_UP);
        } else {
            return commodity.getDiscountValue().multiply(new BigDecimal(quantity));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> payError(String bid, int payStatus) {
        UserStrokeOrderDTO userStrokeOrderDTO = Optional.ofNullable(userStrokeOrderService.getByIdDto(bid)).orElseThrow(() -> new ApiException("订单ID值错误"));
        if (userStrokeOrderDTO.getPayStatus().equals(payStatus)) {
            return ApiResult.error("请勿重复操作");
        }
        if (userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_1) || userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_2)) {
            return ApiResult.success("没有权限操作");
        }
        userStrokeOrderDTO.setPayStatus(payStatus);
        userStrokeOrderService.update(userStrokeOrderDTO);
        userStrokeOrderDTO.freeData();
        return ApiResult.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public void buildCommodity(String bid, Integer payStatus) {
        UserStrokeOrderDTO userStrokeOrderDTO = userStrokeOrderService.getByIdDto(bid);
        if (null == userStrokeOrderDTO) {
            return;
        }
        if (userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
            return;
        }
        userStrokeOrderDTO.setPayStatus(payStatus);
        userStrokeOrderService.update(userStrokeOrderDTO);
        if (payStatus.equals(DisplayUtil.PAY_STATUS_1)) {
            UserStrokeOrderMall userStrokeOrderMall = userStrokeOrderMallService.getById(userStrokeOrderDTO.getOrderId());
            if (null != userStrokeOrderMall) {
                ApiResultWrapper<Object> dcOrder = createDcOrder(userStrokeOrderDTO, userStrokeOrderMall);
                updateUserStrokeOrderMall(userStrokeOrderMall, dcOrder);
                saveCommodityStatistics(userStrokeOrderMall.getCommodityId(), userStrokeOrderDTO.getUserId());
            }
        }
        userStrokeOrderDTO.freeData();
    }

    private void saveCommodityStatistics(String commodityId, String userId) {
        CommodityStatistics commodityStatistics = new CommodityStatistics();
        commodityStatistics.setCommodityId(commodityId);
        if (StringUtils.hasLength(userId)) {
            commodityStatistics.setUserId(userId);
        }
        commodityStatistics.setAddTime(LocalDateTime.now());
        commodityStatistics.setProductStatisticsType(PandaConstant.PRODUCT_STATISTICS_TYPE_2);
        commodityStatistics.setSyncState(PandaConstant.SYNC_STATE_0);
        asyncService.saveCommodityStatistics(commodityStatistics);
    }

    private ApiResultWrapper<Object> createDcOrder(UserStrokeOrderDTO userStrokeOrderDTO, UserStrokeOrderMall userStrokeOrderMall) {
        MallOrderReqDTO mallOrderReqDTO = new MallOrderReqDTO();
        mallOrderReqDTO.setOrderId(userStrokeOrderDTO.getOrderId());
        mallOrderReqDTO.setPayAmount(userStrokeOrderDTO.getPayAmount());
        mallOrderReqDTO.setCommodityId(userStrokeOrderMall.getCommodityId());
        mallOrderReqDTO.setRechargeAccount(userStrokeOrderMall.getRechargeAccount());
        mallOrderReqDTO.setRechargeValue(userStrokeOrderMall.getQuantity());
        ApiResultWrapper<Object> dcOrder = createDcOrder(mallOrderReqDTO);
        mallOrderReqDTO.freeData();
        return dcOrder;
    }

    private void updateUserStrokeOrderMall(UserStrokeOrderMall userStrokeOrderMall, ApiResultWrapper<Object> dcOrder) {
        log.info("调用数据中心接口购买商品,orderId: {},isSuccess:{},msg: {}", userStrokeOrderMall.getOrderId(), dcOrder.isSuccess(), dcOrder.getMsg());
        UserStrokeOrderMallDTO userStrokeOrderMallDTO = new UserStrokeOrderMallDTO();
        userStrokeOrderMallDTO.setOrderId(userStrokeOrderMall.getOrderId());
        userStrokeOrderMallDTO.setOrderState(dcOrder.getCode().equals(ApiResultWrapper.SUCCESS) ? DisplayUtil.PAY_STATUS_1 : DisplayUtil.PAY_STATUS_2);
        userStrokeOrderMallService.update(userStrokeOrderMallDTO);
        userStrokeOrderMallDTO.freeData();
    }

    public UserStrokeOrderDTO payDetail(@NotNull(message = "业务ID不能为空") String thirdPartyOrder) {
        return userStrokeOrderService.getByIdDto(thirdPartyOrder);
    }
}
